package fregat.connector.channel;

import fregat.base.net.TCPClient;
import fregat.connector.ConnectorService;

/**
 * Base class for TCP/IP channels.
 *
 * @author Alexander Mann <br><b><i>ECO_L O G</i> &nbsp Logistiksysteme GmbH</b>
 * @version 1.0
 */
public abstract class TCPChannel extends AbstractChannel {
    protected TCPClient client; // TCP/IP client
    protected Watchdog watchdog; // watchdog thread
    private boolean connected; // connection state
    private int timeout; // timeout after send
    private long lastSend; // last time a telegram was sent

    /**
     * Initializes common members of TCP channels.
     *
     * @param name       Configuration name of channel
     * @param standAlone True, if channel is used standalone only
     */
    protected TCPChannel(String name, boolean standAlone) {
        super(name);

        // configuration -->
        if (!standAlone) {
            int timeout = ConnectorService.getConfiguration().getConfigurationInteger("channel.config." + name + ".timeout");
            if (timeout > 0) {
                this.timeout = timeout;
            }
        }
        // <--
    }

    /**
     * Sends data over the channel.
     *
     * @param data Any message
     * @return True, if send was success
     */
    public boolean send(byte[] data) {
        lastSend = System.currentTimeMillis();
        if (watchdog != null) {
            watchdog.reset();
        }

        // wait some time before send
        try {
            Thread.sleep(timeout);
        } catch (InterruptedException ex) {
        }

        return client.send(data);
    }

    /**
     * Stops the communication on the channel.
     */
    public void shutdown() {
        if (watchdog != null) {
            watchdog.shutdown();
        }
        super.shutdown();
    }

    /**
     * Channel has connected or disconnected.
     *
     * @param b True, if conencted
     */
    public void connected(boolean b) {
        connected = b;
        ConnectorService.stateChanged(getName(), b ? 1 : 0);

        if (connected) {
            // communication partner has connected
            String configPath = "channel.config." + getName() + ".watchdog";
            if (ConnectorService.getConfiguration().getConfigurationInteger(configPath) > 0) {

                boolean send = ConnectorService.getConfiguration().getConfigurationBoolean(configPath + ".send");
                boolean receive =ConnectorService.getConfiguration().getConfigurationBoolean(configPath + ".receive");
                if (send & receive)
                	ConnectorService.warn(getName() + ", " + ConnectorService.resource.getString("TCPChannel_connected_1"));
                if(send) {
                    watchdog = new Watchdog(getName()) {
                        protected void process(String data) {
                            sendWatchdog(data);
                        }
                    };
                }

                if (receive) {
                    watchdog = new Watchdog(getName()) {
                        protected void process(String data) {
                            receiveWatchdog(data);
                        }
                    };
                }

                watchdog.start();
            }
            //super.start();

        } else {
            // communication partner has disconnected
            if (watchdog != null) {
                watchdog.shutdown();
            }
            //super.shutdown();
        }

        ConnectorService.debug(toString());

        // inform listeners about connection state
        super.connected(b);
    }

    /**
     * This method is called, when data was received. The received data is
     * forwarded to all connected listeners.
     *
     * @param data Received data
     */
    public boolean received(byte[] data) {
        boolean isWatchdog = false;
        if (watchdog != null) {
            watchdog.reset();
            isWatchdog = watchdog.isWatchdog(data);
        }

        if (isWatchdog) {
            ConnectorService.debug(getName() + ", " +
                    ConnectorService.resource.getString("AbstractChannel_received_1") +
                    " '" + new String(data) + "'");
          return true;
        } else {
            return super.received(data);
        }
    }

    public String toString() {
        String state = super.toString();
        state += " " + ConnectorService.resource.getString("TCPChannel_toString_" + (connected ? "1" : "2"));

        if (client != null && connected) {
            state += " (" + client.toString() + ")";
        }
        return state;
    }

    /**
     * Channel is sending watchdog and resets on any error.
     *
     * @param data
     */
    public void sendWatchdog(String data) {

        if (System.currentTimeMillis() - lastSend < watchdog.getTimeout()) {
            // if data was sent while watchdog data was created in database
            return;
        }

        if (data == null) {
            ConnectorService.warn(getName() + ", " + ConnectorService.resource.getString("TCPChannel_sendWatchdog_3"));
            return;
        }

        if (client == null) {
            ConnectorService.warn(getName() + ", " + ConnectorService.resource.getString("TCPChannel_sendWatchdog_4"));
            return;
        }

        ConnectorService.debug(getName() + ", " +
                ConnectorService.resource.getString("TCPChannel_sendWatchdog_1") +
                " '" + data + "'");

        if (!send(data.getBytes())) {
            // close connections and reset server
            ConnectorService.debug(getName() + ", " +
                    ConnectorService.resource.getString("TCPChannel_sendWatchdog_2"));
            client.disconnect();
        }
    }

    /**
     * Channel didn't receive any telegram and watchdog time expired.
     *
     * @param data Watchdog telegram data
     */
    public void receiveWatchdog(String data) {
        ConnectorService.error(getName() + ", " + ConnectorService.resource.getString("TCPChannel_receiveWatchdog_1"));
        client.disconnect();
    }
}
